package com.ews.onlineexchange.model;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.inject.Inject;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.PostLoad;
import javax.persistence.PrePersist;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.NotNull;

import org.hibernate.validator.constraints.Length;

import com.ews.onlineexchange.controller.BestTradeEvaluationOfBulletinList;
import com.ews.onlineexchange.model.bulletinRequirement.Requirement;

@Entity

@Table (uniqueConstraints = {
		@UniqueConstraint(columnNames = {"supplier_id", "bulletin_id"})
})
public class Trade extends DataEntity {
	/**
	 * 
	 */
	private static final long			serialVersionUID		= 1L;

	private ServiceSupplier				supplier;
	private Bulletin					bulletin;
	private String						serialNumber;

	private boolean						apply;
	private Date						applyTime;
	private boolean						hasVerify;
	private Date						verifyTime;

	private MarginAccount				marginAccount;											// 保证金子账号
	private BigDecimal					paidMargin				= new BigDecimal( 0 );

	private BigDecimal					offer;													// 最新报价
	private Integer						offerCount				= 0;							// 报价次数
	private TradeRating					tradeRating;
	private List<Requirement>			requirements			= new ArrayList<Requirement>();
	private String						cancelReason;											// 采购方取消中介报名时原因

	private TradeReportData				tradeReportData			= new TradeReportData();		// 自由选取上报信息
	private List<BestTradeEvaluation>	bestTradeEvaluations	= new ArrayList<>();

	private String						cancelAudit				= "0";							// 默认为0，只有为0时，采购方才能发起取消报名申请，
	private String						adminRejectReason;										// 管理员驳回原因
	
	public String getAdminRejectReason() {
		return adminRejectReason;
	}

	public void setAdminRejectReason(String adminRejectReason) {
		this.adminRejectReason = adminRejectReason;
	}

	public String getCancelAudit() {
		return cancelAudit;
	}

	public void setCancelAudit(String cancelAudit) {
		this.cancelAudit = cancelAudit;
	}
	
	
	@Transient
	public List<BestTradeEvaluation> getBestTradeEvaluations() {
		return bestTradeEvaluations;
	}

	public void setBestTradeEvaluations(List<BestTradeEvaluation> bestTradeEvaluations) {
		this.bestTradeEvaluations = bestTradeEvaluations;
	}

	@Length(max = 4000)
	public String getCancelReason() {
		return cancelReason;
	}

	public void setCancelReason(String cancelReason) {
		this.cancelReason = cancelReason;
	}

	@ManyToOne
	@NotNull
	public ServiceSupplier getSupplier() {
		return supplier;
	}

	public void setSupplier(ServiceSupplier supplier) {
		this.supplier = supplier;
	}

	@ManyToOne
	@NotNull
	public Bulletin getBulletin() {
		return bulletin;
	}

	public void setBulletin(Bulletin bulletin) {
		this.bulletin = bulletin;
	}

	@Column(unique = true)
	@NotNull
	public String getSerialNumber() {
		return serialNumber;
	}

	public void setSerialNumber(String serialNumber) {
		this.serialNumber = serialNumber;
	}

	public boolean isApply() {
		return apply;
	}

	public void setApply(boolean apply) {
		this.apply = apply;
	}

	@Temporal(TemporalType.TIMESTAMP)
	public Date getApplyTime() {
		return applyTime;
	}
	
	@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
	public List<Requirement> getRequirements() {
		return requirements;
	}

	public void setRequirements(List<Requirement> requirements) {
		this.requirements = requirements;
	}

	public void setApplyTime(Date applyTime) {
		this.applyTime = applyTime;
	}

	public boolean isHasVerify() {
		return hasVerify;
	}

	public void setHasVerify(boolean hasVerify) {
		this.hasVerify = hasVerify;
	}

	@Temporal(TemporalType.TIMESTAMP)
	public Date getVerifyTime() {
		return verifyTime;
	}

	public void setVerifyTime(Date verifyTime) {
		this.verifyTime = verifyTime;
	}

	@OneToOne
	public MarginAccount getMarginAccount() {
		return marginAccount;
	}

	public void setMarginAccount(MarginAccount marginAccount) {
		this.marginAccount = marginAccount;
	}

	@NotNull
	public BigDecimal getPaidMargin() {
		return paidMargin;
	}

	public void setPaidMargin(BigDecimal paidMargin) {
		this.paidMargin = paidMargin;
	}

	@OneToOne(mappedBy = "trade")
	public TradeRating getTradeRating() {
		return tradeRating;
	}

	public void setTradeRating(TradeRating tradeRating) {
		this.tradeRating = tradeRating;
	}

	public BigDecimal getOffer() {
		return offer;
	}

	public void setOffer(BigDecimal offer) {
		this.offer = offer;
	}
	
	public Integer getOfferCount() {
		return offerCount;
	}

	public void setOfferCount(Integer offerCount) {
		this.offerCount = offerCount;
	}

	@Embedded
	public TradeReportData getTradeReportData() {
		return tradeReportData;
	}

	public void setTradeReportData( TradeReportData tradeReportData ) {
		this.tradeReportData = tradeReportData;
	}
	
	@Transient
	public BigDecimal getReportDataOffer() {
		return getTradeReportData().getOffer();
	}
	
	@Transient
	public boolean isWinner() {	
		Trade successTrade = getBulletin().getSuccessTrade();	
		if (this.getId() == null)
			return false;		
		if (successTrade == null)
			return false;
		return this.getId().equals(successTrade.getId());
	}
	
	@Transient
	public boolean supplierCanApply() {
		if(isApply())
			return false;
		if(isHasVerify())
			return false;
		return true;
		
	}
	
	@Transient
	public boolean supplierCanCancel() {
		if(isApply() && isHasVerify() && getBulletin().getChooseTime().after(new Date()) && getBulletin().getEndingApplyTime().after(new Date()))
			return true;
		
		return false;
		
	}
	
	@Transient
	public BigDecimal getSumScore() {
		BigDecimal sum = new BigDecimal( 0 );
		for ( BestTradeEvaluation bestTradeEvaluation : bestTradeEvaluations ) {
			sum = sum.add( bestTradeEvaluation.getScore() );
		}
		BigDecimal offerScore = getTradeReportData().getOfferScore() == null ? new BigDecimal(0) : getTradeReportData().getOfferScore() ;
		sum = sum.add(offerScore);
		return sum;
	}
	
	@PrePersist
	private void preSave() {		
		if ( this.getBulletin().getConflictingSupplierId() != null && this.getBulletin().getConflictingSupplierId().equals( this.getSupplier().getId() ) ) {
			throw new RuntimeException( "ConflictingBulletinException" );
		}
		
		if ( this.getSupplier().getCreditScore().compareTo( getBulletin().getCreditScoreLimit() ) < 0 ) {
			throw new RuntimeException( "CreditScore not enough!" );
		}
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = super.hashCode();
		result = prime * result + (apply ? 1231 : 1237);
		result = prime * result + ((applyTime == null) ? 0 : applyTime.hashCode());		
		result = prime * result + (hasVerify ? 1231 : 1237);
		result = prime * result + ((marginAccount == null) ? 0 : marginAccount.hashCode());
		result = prime * result + ((offer == null) ? 0 : offer.hashCode());
		result = prime * result + ((paidMargin == null) ? 0 : paidMargin.hashCode());
		result = prime * result + ((serialNumber == null) ? 0 : serialNumber.hashCode());
		result = prime * result + ((supplier == null) ? 0 : supplier.hashCode());
//		result = prime * result + ((tradeRating == null) ? 0 : tradeRating.hashCode());
		result = prime * result + ((verifyTime == null) ? 0 : verifyTime.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (!super.equals(obj))
			return false;
		if (getClass() != obj.getClass())
			return false;
		Trade other = (Trade) obj;
		if (apply != other.apply)
			return false;
		if (applyTime == null) {
			if (other.applyTime != null)
				return false;
		} else if (!applyTime.equals(other.applyTime))
			return false;		
		if (hasVerify != other.hasVerify)
			return false;
		if (marginAccount == null) {
			if (other.marginAccount != null)
				return false;
		} else if (!marginAccount.equals(other.marginAccount))
			return false;
		if (offer == null) {
			if (other.offer != null)
				return false;
		} else if (!offer.equals(other.offer))
			return false;
		if (paidMargin == null) {
			if (other.paidMargin != null)
				return false;
		} else if (!paidMargin.equals(other.paidMargin))
			return false;
		if (serialNumber == null) {
			if (other.serialNumber != null)
				return false;
		} else if (!serialNumber.equals(other.serialNumber))
			return false;
		if (supplier == null) {
			if (other.supplier != null)
				return false;
		} else if (!supplier.equals(other.supplier))
			return false;
		if (tradeRating == null) {
			if (other.tradeRating != null)
				return false;
		} else if (!tradeRating.equals(other.tradeRating))
			return false;
		if (verifyTime == null) {
			if (other.verifyTime != null)
				return false;
		} else if (!verifyTime.equals(other.verifyTime))
			return false;
		return true;
	}
}
